package com.mall.service.Impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mall.common.R;
import com.mall.common.RedisIdWorker;
import com.mall.config.RabbitConfig;
import com.mall.entity.*;
import com.mall.entity.dto.ErrorMessage;
import com.mall.entity.dto.SmoothMallCartDto;
import com.mall.entity.dto.SmoothMallOrderDto;
import com.mall.mapper.SmoothMallCartMapper;
import com.mall.mapper.SmoothMallOrderMapper;
import com.mall.service.*;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.concurrent.FailureCallback;
import org.springframework.util.concurrent.SuccessCallback;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@Slf4j
@Service
public class SmoothMallOrderServiceImpl extends ServiceImpl<SmoothMallOrderMapper, SmoothMallOrder> implements SmoothMallOrderService {
    @Autowired
    SmoothMallCartService smoothMallCartService;
    @Autowired
    SmoothMallProductService smoothMallProductService;
    @Autowired
    SmoothMallOrderItemService smoothMallOrderItemService;
    @Autowired
    TbSeckillVoucherService seckillVoucherService;
    @Autowired
    VoucherOrderService voucherOrderService;
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    SendMailService sendMailService;
    @Autowired
    RabbitTemplate rabbitTemplate;
    private SmoothMallOrderService currentProxy;
    @Lazy
    @Autowired
    public void setSmoothMallOrderService(SmoothMallOrderService smoothMallOrderService) {
        this.currentProxy = smoothMallOrderService;
    }
    //创建脚本
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
//    //创建线程池
//    private static final ExecutorService EXECUTOR_SECKILL_SERVICE= Executors.newSingleThreadExecutor();
//    @PostConstruct
//    private void init(){
//        EXECUTOR_SECKILL_SERVICE.submit(new createSecKillOrder());
//    }
//    //创建阻塞队列
//    private final BlockingQueue<VoucherOrder> blockingQueue=new ArrayBlockingQueue<>(1024*1024) ;

    static {
        SECKILL_SCRIPT = new DefaultRedisScript();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("Seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }
    @Autowired
    RedisIdWorker redisIdWorker;
    @Override
    public R seckillVoucher(Long voucherId) {
        Integer userId = getUserId();
        Long result = stringRedisTemplate.execute
                (
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), userId.toString()
        );
        if (result!=0){
            return R.error(result.intValue() == 1 ? "库存不足" : "不能重复下单");
        }
        //6. 创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        long orderId = redisIdWorker.nextId("order");
        //6.1 设置订单id
        //6.2 设置用户id
        //6.3 设置代金券id
        voucherOrder.setVoucherId(voucherId);
        voucherOrder.setOrderNo(orderId);
        voucherOrder.setUserId(userId);
        voucherOrder.setOrderStatus(0);
        String jsonString = JSON.toJSONString(voucherOrder);
        //准备correlationData，配置每个消息都有一个全局唯一id，并准备回调方法
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        correlationData.getFuture().addCallback(result1 -> {
            //判断结果
            if (result1.isAck()){
                log.debug("消息成功投递到交换机！消息id:{}",correlationData.getId());
            }else {
                log.error("消息投递到交换机失败！消息id:{}",correlationData.getId());
                try {
                    Thread.sleep(5000);
                    rabbitTemplate.convertAndSend(RabbitConfig.MALL_EXCHANGE,"mall.seckill.order",jsonString,correlationData);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, ex -> {
            log.error("消息发送失败",ex);
            try {
                Thread.sleep(5000);
                rabbitTemplate.convertAndSend(RabbitConfig.MALL_EXCHANGE,"mall.seckill.order",jsonString,correlationData);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //7. 将订单数据保存到表中
        rabbitTemplate.convertAndSend(RabbitConfig.MALL_EXCHANGE,"mall.seckill.order",jsonString,correlationData);
        return R.success(orderId);
    }

    @NotNull
    @Transactional
    @Override
    public void createVoucherOrder(VoucherOrder voucherOrder) {
        Long count = voucherOrderService.query().eq("voucher_id", voucherOrder.getVoucherId()).eq("user_id", voucherOrder.getUserId()).count();
            if (count > 0){
                log.error("库存不足");
                return;
            }
            //5. 扣减库存
            boolean success = seckillVoucherService.update()
                    .setSql("stock = stock - 1")
                    .eq("voucher_id", voucherOrder.getVoucherId())
                    .gt("stock",0)
                    .update();
            if (!success) {
                log.error("已经抢过优惠券了哦");
                return;
            }
            //7. 将订单数据保存到表中
            voucherOrderService.save(voucherOrder);
    }
    //监听订单队列，获取消息后创建订单
    @RabbitListener(queues = RabbitConfig.MALL_QUEUE)
    public void createSeckillOrder(Message message){
        byte[] body = message.getBody();
        String jsonString = new String(body);
        VoucherOrder voucherOrder = JSON.parseObject(jsonString, VoucherOrder.class);
//        System.out.println(1/0);
        createVoucherOrder(voucherOrder);
    }
    //接收错误的消息队列信息
    @RabbitListener(queues = "error.queue")
    public void errorMessageHandle(Message message){
        ErrorMessage errorMessageToMail = new ErrorMessage();//创建一个封装错误的对象
        byte[] body = message.getBody();//获得消息体
        String jsonString = new String(body);//将消息体转化为json数据
        VoucherOrder voucherOrder = JSON.parseObject(jsonString, VoucherOrder.class);//转化为优惠券订单尸体来
        MessageProperties messageProperties = message.getMessageProperties();
        Map<String, Object> headers = messageProperties.getHeaders();//获取头信息
        String errorMessage = headers.get("x-exception-message").toString();//根据头信息获取异常原因信息
        //依次封装
        errorMessageToMail.setUserId(voucherOrder.getUserId());
        errorMessageToMail.setOrderId(voucherOrder.getOrderNo());
        errorMessageToMail.setErrorReason(errorMessage);
        errorMessageToMail.setErrorTime(LocalDateTime.now().toString());
        errorMessageToMail.setMessageId(messageProperties.getHeader("correlation_id"));
        sendMailService.sendMailForErrorMessage(errorMessageToMail.toString());
    }
    //接收超时订单的消息队列信息
    @Transactional
    @RabbitListener(queues = "mall.order.dlx")
    public void TimeOutHandle(Message message){
        byte[] body = message.getBody();//获得消息体
        String orderNo = new String(body);//将消息体转化为json数据
        SmoothMallOrder mallOrder = getOne(new LambdaQueryWrapper<SmoothMallOrder>().eq(SmoothMallOrder::getOrderNo, orderNo));
        //获得此时订单的支付状态
        Integer orderStatus = mallOrder.getOrderStatus();
        if (orderStatus==10){//订单付款未付款
            deleteOrderForOrderNo(orderNo);
        }
    }
    @Override
    public boolean createOrder(SmoothMallOrder smoothMallOrder) {
        //生成订单列表的成功状态
        boolean save1=true;
        //获得用户id
        Integer userId = getUserId();
        //获得订单id
        String orderNo = UUID.randomUUID().toString();
        //获取购物车选中的所有商品
        int totalPrice=0;
        LambdaQueryWrapper<SmoothMallCart> queryWrapper = new LambdaQueryWrapper<SmoothMallCart>()
                .eq(SmoothMallCart::getUserId, userId)
                .eq(SmoothMallCart::getSelected, 1);
        List<SmoothMallCart> mallCartList = smoothMallCartService.list(queryWrapper);
        //清空购物车
        boolean remove = smoothMallCartService.remove(queryWrapper);
        //计算总价
        for (int i = 0; i < mallCartList.size(); i++) {
            //获取购物车的每个商品列
            SmoothMallCart smoothMallCart = mallCartList.get(i);
            //获取商品详细信息
            SmoothMallProduct mallProduct = smoothMallProductService.getById(smoothMallCart.getProductId());
            //计算该商品总价
            Integer oneTotal =mallProduct .getPrice() * smoothMallCart.getQuantity();
            //计算订单总价
            totalPrice=totalPrice+oneTotal;
            //封装订单列表
            SmoothMallOrderItem orderItem = new SmoothMallOrderItem();
            orderItem.setOrderNo(orderNo);
            orderItem.setProductId(mallProduct.getId());
            orderItem.setProductName(mallProduct.getName());
            orderItem.setProductImg(mallProduct.getImage());
            orderItem.setUnitPrice(mallProduct.getPrice());
            orderItem.setQuantity(smoothMallCart.getQuantity());
            orderItem.setTotalPrice(oneTotal);
            save1 = smoothMallOrderItemService.save(orderItem);
        }
        smoothMallOrder.setOrderNo(orderNo);
        smoothMallOrder.setUserId(userId);
        smoothMallOrder.setTotalPrice(totalPrice);
        //发送到一个过期时间为30s的延迟队列，开启超时支付监控
        rabbitTemplate.convertAndSend("mall.order.exchange","order",orderNo);
        boolean save2 = save(smoothMallOrder);
        return save1&&save2&&remove;
    }

    @Override
    public List<SmoothMallOrderDto> getDtoList() {
        Integer userId = getUserId();
        //构建返回对象，即所有的订单信息
        ArrayList<SmoothMallOrderDto> orderDtos = new ArrayList<>();
        //查看当前用户的所有订单信息
        List<SmoothMallOrder> orders = list(new LambdaQueryWrapper<SmoothMallOrder>()
                .eq(SmoothMallOrder::getUserId, userId));
        orders.stream().forEach(order->{
            //获取每个订单的所有商品
            List<SmoothMallOrderItem> items = smoothMallOrderItemService.list(new LambdaQueryWrapper<SmoothMallOrderItem>()
                    .eq(SmoothMallOrderItem::getOrderNo, order.getOrderNo()));
            //创建一个集合，存储每个订单的所有商品的
            ArrayList<SmoothMallOrderItem> orderItems = new ArrayList<>();
            //遍历每个商品存放在集合里
            items.stream().forEach(i->{
                orderItems.add(i);
            });
            SmoothMallOrderDto smoothMallOrderDto = new SmoothMallOrderDto();
            smoothMallOrderDto.setSmoothMallOrder(order);
            smoothMallOrderDto.setOrderItemVOList(orderItems);
            if (order.getOrderStatus()==10){
                smoothMallOrderDto.setOrderStatusName("未付款");
            }
            if (order.getOrderStatus()==20){
                smoothMallOrderDto.setOrderStatusName("已付款");
            }
            if (order.getOrderStatus()==30){
                smoothMallOrderDto.setOrderStatusName("已发货");
            }
            if (order.getOrderStatus()==40){
                smoothMallOrderDto.setOrderStatusName("交易完成");
            }
            orderDtos.add(smoothMallOrderDto);
        });
        return orderDtos;
    }

    @Override
    public List<SmoothMallOrderDto> getAdminDtoList() {
        //构建返回对象，即所有的订单信息
        ArrayList<SmoothMallOrderDto> orderDtos = new ArrayList<>();
        //查看当前用户的所有订单信息
        List<SmoothMallOrder> orders = list();
        orders.stream().forEach(order->{
            //获取每个订单的所有商品
            List<SmoothMallOrderItem> items = smoothMallOrderItemService.list(new LambdaQueryWrapper<SmoothMallOrderItem>()
                    .eq(SmoothMallOrderItem::getOrderNo, order.getOrderNo()));
            //创建一个集合，存储每个订单的所有商品的
            ArrayList<SmoothMallOrderItem> orderItems = new ArrayList<>();
            //遍历每个商品存放在集合里
            items.stream().forEach(i->{
                orderItems.add(i);
            });
            SmoothMallOrderDto smoothMallOrderDto = new SmoothMallOrderDto();
            smoothMallOrderDto.setSmoothMallOrder(order);
            smoothMallOrderDto.setOrderItemVOList(orderItems);
            if (order.getOrderStatus()==10){
                smoothMallOrderDto.setOrderStatusName("未付款");
            }
            if (order.getOrderStatus()==20){
                smoothMallOrderDto.setOrderStatusName("已付款");
            }
            if (order.getOrderStatus()==30){
                smoothMallOrderDto.setOrderStatusName("已发货");
            }
            if (order.getOrderStatus()==40){
                smoothMallOrderDto.setOrderStatusName("交易完成");
            }
            orderDtos.add(smoothMallOrderDto);
        });
        return orderDtos;
    }

    @Override
    public boolean orderDelivered(String orderNo) {
        SmoothMallOrder mallOrder = getOne(new LambdaQueryWrapper<SmoothMallOrder>().eq(SmoothMallOrder::getOrderNo, orderNo));
        LocalDateTime currentDateTime = LocalDateTime.now();//当前时间
        LocalDateTime plus = currentDateTime.plus(5, ChronoUnit.MINUTES);
        mallOrder.setDeliveryTime( currentDateTime);
        mallOrder.setPayTime(plus);
        mallOrder.setOrderStatus(30);
        boolean update = saveOrUpdate(mallOrder);
        return update;
    }

    @Override
    public boolean orderFinished(String orderNo) {
        SmoothMallOrder mallOrder = getOne(new LambdaQueryWrapper<SmoothMallOrder>().eq(SmoothMallOrder::getOrderNo, orderNo));
        LocalDateTime currentDateTime = LocalDateTime.now();//当前时间
        mallOrder.setOrderStatus(40);
        mallOrder.setEndTime(currentDateTime);
        boolean update = saveOrUpdate(mallOrder);
        return update;
    }

    @Override
    public boolean deleteOrderForOrderNo(String orderNo) {
        boolean remove = remove(new LambdaQueryWrapper<SmoothMallOrder>().eq(SmoothMallOrder::getOrderNo, orderNo));
        boolean remove1 = smoothMallOrderItemService.remove(new LambdaQueryWrapper<SmoothMallOrderItem>().eq(SmoothMallOrderItem::getOrderNo, orderNo));
        return remove&&remove1;
    }


    private Integer getUserId() {
        //生成订单id //从上下文中获取用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser principal = (LoginUser) authentication.getPrincipal();
        Integer userId = principal.getSmoothMallUser().getId();
        return userId;
    }
//    private class createSecKillOrder implements Runnable{
//        @Override
//        public void run() {
//            while (true) {
//                try {
//                    VoucherOrder voucherOrder = blockingQueue.take();
//                    currentProxy.createVoucherOrder(voucherOrder);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }
//        }
//    }
//    @NotNull
//    @Transactional
//    @Override
//    public R createVoucherOrder(Long voucherId) {
//        Integer id =12615686;//getUserId();
//        Long count = voucherOrderService.query().eq("voucher_id", voucherId).eq("user_id", id).count();
//        if (count > 0){
//            return R.error("你已经抢过优惠券了哦");
//        }
//        //5. 扣减库存
//        boolean success = seckillVoucherService.update()
//                .setSql("stock = stock - 1")
//                .eq("voucher_id", voucherId)
//                .gt("stock",0)
//                .update();
//        if (!success) {
//            return R.error("库存不足");
//        }
//        //6. 创建订单
//        VoucherOrder voucherOrder = new VoucherOrder();
//        //6.1 设置订单id
//        long orderId = redisIdWorker.nextId("order");
//        //6.2 设置用户id
//        //6.3 设置代金券id
//        voucherOrder.setVoucherId(voucherId);
//        voucherOrder.setOrderNo(orderId);
//        voucherOrder.setUserId(id);
//        voucherOrder.setOrderStatus(0);
//        //7. 将订单数据保存到表中
//        voucherOrderService.save(voucherOrder);
//        //8. 返回订单id
//        return R.success(orderId);
//
//    }
    //    @Override
//    public R seckillVoucher(Long voucherId) {
//        LambdaQueryWrapper<TbSeckillVoucher> queryWrapper = new LambdaQueryWrapper<>();
//        //1. 查询优惠券
//        queryWrapper.eq(TbSeckillVoucher::getVoucherId, voucherId);
//        TbSeckillVoucher seckillVoucher = seckillVoucherService.getOne(queryWrapper);
//        //2. 判断秒杀时间是否开始
//        if (LocalDateTime.now().isBefore(seckillVoucher.getBeginTime())) {
//            return R.error("秒杀还未开始，请耐心等待");
//        }
//        //3. 判断秒杀时间是否结束
//        if (LocalDateTime.now().isAfter(seckillVoucher.getEndTime())) {
//            return R.error("秒杀已经结束！");
//        }
//        //4. 判断库存是否充足
//        if (seckillVoucher.getStock() < 1) {
//            return R.error("优惠券已被抢光了哦，下次记得手速快点");
//        }
//        Integer id =12615686;//getUserId();
//        RLock lock = redissonClient.getLock(id.toString().intern());
//        lock.tryLock();
//        try {
//            return currentProxy.createVoucherOrder(voucherId);
//        } finally {
//            lock.unlock();
//        }
//    }
}
